﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SmartRoute.BingMaps.ImageryService;
using SmartRoute.Data;
using System.ComponentModel;

namespace SmartRoute.BingMaps
{
    public class Imagery : INotifyPropertyChanged
    {
        //API key used for BING maps API
        String  key = "AhqjOSdmQlNyiAfMcr8HicreBdxgTnuVaiMGVFnP9x3l_12WQgLyV1pRrfIpECX5";
        
        //Transparency of the randomly generated colors
        const byte Alpha = 212;

        //used to move map around on screen
        double offsetY;
        double offsetX;
        int distanceFromEdgeToCenterOfMapX;
        int distanceFromEdgeToCenterOfMapY;
        double centerLat;
        double centerLon;
        int mapHeight;
        int mapWidth;
        MapStyle style;
        TranslateTransform lineShiftingTransform;

        DrawingGroup testGroup = new DrawingGroup();

        public DrawingGroup TestGroup
        {
            get { return testGroup; }
            set { testGroup = value; }
        }

        GeometryGroup drawnRoutesGrouping;
        //getter and setter for Canvas

        public GeometryGroup DrawnRoutesGrouping
        {
            
            get { return drawnRoutesGrouping; }
        }

        ImageSource mapImage;

        public ImageSource MapImage
        {
            get { return mapImage; }
        }

        Color[] colors; //temporary

        List<PathGeometry> drawnRoutes;

        List<SmartRoute.Data.Route> routes;

        public List<SmartRoute.Data.Route> Routes
        {
            get { return routes; }
            set { routes = value; }
        }

        int zoomLevel;

        //constructor for new map Imagery module
        public Imagery(IEnumerable<Route> inRoutes)
        {
            IEnumerable<Route> temp =
                from Route inRoute in inRoutes
                where inRoute.Directions != null
                select inRoute;

            this.routes = temp.ToList();

            offsetY = 0;
            offsetX = 0;
            style = MapStyle.Road;
            zoomLevel = 11;
            mapHeight = 800;
            mapWidth = 800;
            drawnRoutes = new List<PathGeometry>();
            
            colors = new Color[100];
            Random ran = new Random(2);
            drawnRoutesGrouping = new GeometryGroup();
            
            
           //Originally, four integers were generated and cast.
            //Changed it to this for efficiency and simplicity

            //Hylomorphism's state field, an array of 3 bytes R, G, B
            byte[] colorBytes = new byte[3];
            for (int i = 0; i < 100; i++)
            {
                ran.NextBytes(colorBytes);
                colors[i] = Color.FromArgb(Alpha           //A, See remark below
                                          ,colorBytes[0]   //R, 
                                          ,colorBytes[1]   //G,
                                          ,colorBytes[2]); //B.
            } //You definitely did not intend A to be random. 
              //A low or 0 A makes a color invisible.
            this.assembleCanvas();
        }


        //assembles map image and routes onto canvas// this should happen only once unless the zoom level is changed,
        //in future versions it will assemble the initial canvas and display it and then in the 
        //background assemble canvases for the other zoom levels.  Most of the bottleneck is recalculating
        //the waypoints.  The other option would be to figure out how to calculate the routes for one zoom level
        //and then adjust them for other zoom levels, but that would require redoing the calculations and
        //I am not sure how hard this is.
        public void assembleCanvas()
        {
            //sets the center of the map to be the depot
            SmartRoute.Data.Location center = routes[0].Source;
            //gets map image with parameters associated with module instance
            mapImage = getBaseMapImage(center).Source;
            
          
            //store the center lat and lon
            this.centerLat = center.Latitude;
            this.centerLon = center.Longitude;

            //find how the center in terms of x and y
            int centerX = convertLongToX(center.Longitude);
            int centerY = convertLatToY(center.Latitude);

            //find distance from center to edge of canvas
            distanceFromEdgeToCenterOfMapX = (mapWidth / 2) - centerX;
            distanceFromEdgeToCenterOfMapY = (mapWidth / 2) - centerY;

            //keeps track of the color for the next route
            int nextColor = 0;

            
            //goes through routes and displays as lines on the map
            foreach (Route route in routes)
            {
                double lastLatProcessed = route.Waypoints.ElementAt(0).Latitude;
                double lastLonProcessed = route.Waypoints.ElementAt(0).Longitude;

                SolidColorBrush myBrush = new SolidColorBrush();

                {
                    myBrush.Color = colors[nextColor];
                    nextColor++;
                }


                
                this.lineShiftingTransform = new TranslateTransform();
                this.lineShiftingTransform.X = distanceFromEdgeToCenterOfMapX;
                this.lineShiftingTransform.Y = distanceFromEdgeToCenterOfMapY;
                
                PathGeometry path = new PathGeometry();
                GeometryDrawing newDraw = new GeometryDrawing();
                newDraw.Geometry = path;
                newDraw.Brush = myBrush;
                path.Transform = this.lineShiftingTransform;

                Point start = new Point();
                start.X = convertLongToX(route.Waypoints.ToArray()[0].Longitude);
                start.Y = convertLatToY(route.Waypoints.ToArray()[0].Latitude);
                
                for (int i = 0; i < route.Waypoints.Count(); i++)
                {
                    LineGeometry lineSegments = new LineGeometry();
                    
                    Point nextPoint = new Point();

                    lastLatProcessed = route.Waypoints.ElementAt(i).Latitude;
                    lastLonProcessed = route.Waypoints.ElementAt(i).Longitude;

                    //calculate x and y pixels at end of line
                    int xEnd = convertLongToX(lastLonProcessed);
                    int yEnd = convertLatToY(lastLatProcessed);

                    nextPoint.X = xEnd;
                    nextPoint.Y = yEnd;

                    lineSegments.StartPoint = start;
                    lineSegments.EndPoint = nextPoint;
                    start = nextPoint;
                    path.AddGeometry(lineSegments);
                    
                }
                this.drawnRoutes.Add(path);
                //this.drawnRoutesGrouping.Children.Add(path);
                this.testGroup.Children.Add(newDraw);
            }
        }

        public void notifyTest()
        {
            Notify("MapImage");
            Notify("DrawnRoutesGrouping");
        }

        public void shiftImage(double latShift, double lonShift)
        {
            double newCenterLat = this.centerLat + latShift;
            double newCenterLon = this.centerLon + lonShift;

            SmartRoute.Data.Location newCenter = new SmartRoute.Data.Location(newCenterLat, newCenterLon, "");

            mapImage = getBaseMapImage(newCenter).Source;
            //new offset
            this.distanceFromEdgeToCenterOfMapX = (mapWidth / 2) - convertLongToX(newCenterLon);
            this.distanceFromEdgeToCenterOfMapY = (mapWidth / 2) - convertLatToY(newCenterLat);

            TranslateTransform newShift = new TranslateTransform();
            newShift.X = distanceFromEdgeToCenterOfMapX;
            newShift.Y = distanceFromEdgeToCenterOfMapY;

            this.lineShiftingTransform = newShift;
            
            Notify("MapImage");
            Notify("DrawnRoutesGrouping");
            

        }
        //turns lines on the map on and off
        public void setVisible(Boolean willDisplay, int lineNumber)
        {
            if (willDisplay == false)
            {
                drawnRoutesGrouping.Children.Remove(drawnRoutes[lineNumber]);
                
            }
            else
            {
                if (drawnRoutesGrouping.Children.Contains(drawnRoutesGrouping.Children[lineNumber]))
                {
                    drawnRoutesGrouping.Children.Add(drawnRoutes[lineNumber]);
                    
                }
            }
        }

        //converts a latitude to a Y coordinate given zoom level, center, and offset associated with current instance
        public int convertLatToY(double lat)
        {
            int mapSize = 256 << zoomLevel; 

            double sinLat = Math.Sin(lat * Math.PI / 180);

            double y = .5 - Math.Log((1 + sinLat) / (1 - sinLat)) / (4 * Math.PI);

            int pixelY = (int)(y * mapSize + .5);

            return pixelY;

        }

        public int convertLongToX(double lon)
        {
            int mapSize = 256 << zoomLevel; 

            double x = (lon + 180) / 360;

            int pixelX = (int)(x * mapSize + .5);

            return pixelX;

        }

        public Image getBaseMapImage(SmartRoute.Data.Location center)
        {
            MapUriRequest mapUriRequest = new MapUriRequest();

            // Set credentials using a valid Bing Maps key
            mapUriRequest.Credentials = new ImageryService.Credentials();
            mapUriRequest.Credentials.ApplicationId = key;

            // Set the location of the requested image
            mapUriRequest.Center = new ImageryService.Location();
            mapUriRequest.Center.Latitude = center.Latitude + offsetX;
            mapUriRequest.Center.Longitude = center.Longitude + offsetY;

            // Set the map style and zoom level
            MapUriOptions mapUriOptions = new MapUriOptions();
            mapUriOptions.Style = style;
            mapUriOptions.ZoomLevel = zoomLevel;

            // Set the size of the requested image in pixels
            mapUriOptions.ImageSize = new ImageryService.SizeOfint();
            mapUriOptions.ImageSize.Height = mapHeight;
            mapUriOptions.ImageSize.Width = mapWidth;

            mapUriRequest.Options = mapUriOptions;



            /*
            // Set the location of the requested image
            BingMaps.ImageryService.Location initial = new BingMaps.ImageryService.Location();
            initial.Latitude = testModule.accessMap(0).Routes[0, 1a].Result.Legs[0].ActualStart.Latitude;
            initial.Longitude = testModule.accessMap(0).Routes[0, 1].Result.Legs[0].ActualStart.Longitude;
            // Set the location of the requested image
            MapAppLearning.ImageryService.Location dest = new MapAppLearning.ImageryService.Location();
            dest.Latitude = testModule.accessMap(0).Routes[0, 1].Result.Legs[0].ActualEnd.Latitude;
            dest.Longitude = testModule.accessMap(0).Routes[0, 1].Result.Legs[0].ActualEnd.Longitude;

            //mapUriRequest.MajorRoutesDestination.Latitude = routes[0, 0].Result.Legs[0].ActualEnd.Latitude;
            //mapUriRequest.MajorRoutesDestination.Longitude = routes[0, 0].Result.Legs[0].ActualEnd.Longitude;
            */


            //Make the request and return the URI
            ImageryServiceClient imageryService = new ImageryServiceClient("BasicHttpBinding_IImageryService");
            MapUriResponse mapUriResponse = imageryService.GetMapUri(mapUriRequest);
            
            //Get URI and set image
            String imageURI = mapUriResponse.Uri;

            BitmapImage mapImage = new BitmapImage(new Uri(imageURI));
            Image image = new Image();
            image.Source = mapImage;

            return image;

        }

        

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void Notify(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

    }

}
